#include "thread.h"
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdio.h>

Thread threads[4];

thread_t current_thread;

extern void return_stub();

void 
thread_end(void * ret) {
    threads[current_thread].ret = ret;
    threads[current_thread].status = TERMINATED;
    // free the memory
    free(threads[current_thread].stack);
    thread_yield();
}

/**
 * 弹出frame, 执行对应线程
 */
static void pop_frame(Frame* pframe) {
    asm("movq %0, %%rax;"
            "mov %%rax, %%rsp;"
            "add $8, %%rsp;"
            "pop %%rdi;"
            "pop %%rsi;"
            "pop %%rbp;"
            "pop %%rbx;"
            "pop %%rdx;"
            "pop %%rcx;"
            "pop %%rax;"
            "popf;"
            "ret"
            : 
            : "m"(pframe)
            :);
}

/**
 * 这个函数不会被显式调用, 而是在thread_yield中调用
 * 作用是调度线程
 */
void 
schedule(Frame frame) {
    // 保存状态
    threads[current_thread].frame = frame;
    for (int i = current_thread+1 ; ; i = (i+1)&(MAX_THREAD-1)) {
        if (threads[i].status == RUNNABLE) {
            current_thread = i;
            pop_frame((Frame*)(threads[i].frame.rsp));
        }
    }
    // 永远不会返回
}

int
thread_create(thread_t* thread, void*(*start)(void*), void* arg) {
    // thread 0是主线程
    threads[0].status = RUNNABLE;

    unsigned freei = 1;

    while(freei < MAX_THREAD && threads[freei].status == RUNNABLE)
        freei++;

    if (freei == MAX_THREAD) {
        fprintf(stderr, "not enough thread resources\n");
        return -1;
    }

    threads[freei].thread_id = freei;

    // 分配栈空间
    char* stack = (char*)malloc(STACK_SIZE);
    memset(&threads[freei].frame, 0, sizeof(Frame));

    threads[freei].frame.rsp = (uint64_t)stack+STACK_SIZE-sizeof(Frame);  
    // 栈向下生长
    
    // 线程返回地址
    threads[freei].frame.exit_rip = (uint64_t)return_stub;

    // 参数
    // threads[freei].frame.arg = (uint64_t)arg;
    // 不能通过栈传, 64位传参使用寄存器
    threads[freei].frame.rdi = (uint64_t)arg;

    // 设置rip
    threads[freei].frame.rip = (uint64_t)start;

    memcpy((char*)(threads[freei].frame.rsp),
            &threads[freei].frame, sizeof(Frame));
    // 标记可运行
    threads[freei].status = RUNNABLE;

    thread_yield();
    //pop_frame((Frame*)threads[freei].frame.rsp);

    *thread = freei;

    return 0;
}

int 
thread_join(thread_t thread, void** ret) {
    while(threads[thread].status == RUNNABLE) {
        thread_yield();
    }
    if (ret) {
        *ret = (void*)threads[thread].ret;
    }
    return 0;
}
